﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using OpenTK;
using OpenTK.Input;

using System.Runtime.InteropServices;
using System.IO;

#if EMBEDDED
    using OpenTK.Graphics.ES20;
#else
using OpenTK.Graphics.OpenGL;
#endif

namespace MonoStrategy.RenderSystem
{
    public partial class TerrainRenderer
    {
        public Rectangle ComputeOcclusion(Matrix4 inProjMatrix, Matrix4 inViewMatrix, Matrix4 inModelMatrix)
        {
            m_OcclusionShader.Bind(inProjMatrix, inViewMatrix, inModelMatrix);

            return m_Mesh.ComputeOcclusion();
        }

        internal bool MouseToGridPos(Point inMouseXY, Matrix4 inProjMatrix, Matrix4 inViewMatrix, Matrix4 inModelMatrix, out Point outGridPos)
        {
            outGridPos = new Point(-1, -1);

            GL.ClearColor(Color.White);
            GL.Clear(ClearBufferMask.ColorBufferBit);

            // render selection
            m_SelectionShader.Bind(inProjMatrix, inViewMatrix, inModelMatrix);

            GL.Uniform1(m_SelectionOffsetXID, (float)ScreenBounds.X);
            GL.Uniform1(m_SelectionOffsetYID, (float)ScreenBounds.Y); GLRenderer.CheckError();

            m_Mesh.RenderBlocks(ScreenBounds, false); GLRenderer.CheckError();

            // read pixel at mouse position
            int[] selectedPixels = new int[1];
            int pixel;

            GL.ReadPixels(inMouseXY.X, Renderer.ViewportHeight - inMouseXY.Y, 1, 1, PixelFormat.Rgb, PixelType.UnsignedByte, selectedPixels);

            pixel = selectedPixels[0];

            // convert back to grid pos
            if ((pixel & 0xFF0000) != 0)
                return false;

            outGridPos = new Point(
                ScreenBounds.X + (pixel & 0xFF),
                ScreenBounds.Y + ((pixel & 0xFF00) >> 8));

            return true;
        }

        private String ParameterizeFragmentShader(String inShaderSource, XMLTerrainConfig inParams)
        {
            String parameters =
                "#define PARAMETERIZED\t\n" +
                "#define HEIGHTSCALE		" + FormatDouble(inParams.HeightScale) + "\r\n" +
                "#define NORMALSCALE		" + FormatDouble(inParams.NormalZScale) + "\r\n" +
                "#define WATERHEIGHT		" + FormatDouble(inParams.Water.Height) + "\r\n" +
                "#define MAPSIZE			" + FormatDouble(Size) + "\r\n" +
                "#define RED_FREQ		" + FormatDouble(inParams.RedNoiseFrequency) + "\r\n" +
                "#define GREEN_FREQ		" + FormatDouble(inParams.RedNoiseFrequency) + "\r\n" +
                "#define BLUE_FREQ		" + FormatDouble(inParams.RedNoiseFrequency) + "\r\n" +
                "" + "\r\n" +
                "#define TEXSCALE_00		" + FormatDouble(inParams.Levels[0].TextureScale) + "\r\n" +
                "#define TEXSCALE_01		" + FormatDouble(inParams.Levels[1].TextureScale) + "\r\n" +
                "#define TEXSCALE_02		" + FormatDouble(inParams.Levels[2].TextureScale) + "\r\n" +
                "#define TEXSCALE_03		" + FormatDouble(inParams.Levels[3].TextureScale) + "\r\n" +
                "#define TEXSCALE_04		" + FormatDouble(inParams.Levels[4].TextureScale) + "\r\n" +
                "" + "\r\n" +
                "#define REDNOISESCALE_00			" + FormatDouble(inParams.Levels[0].RedNoiseDivisor) + "\r\n" +
                "#define GREENNOISESCALE_00			" + FormatDouble(inParams.Levels[0].GreenNoiseDivisor) + "\r\n" +
                "#define BLUENOISESCALE_00			" + FormatDouble(inParams.Levels[0].BlueNoiseDivisor) + "\r\n" +
                "#define REDNOISESCALE_01			" + FormatDouble(inParams.Levels[1].RedNoiseDivisor) + "\r\n" +
                "#define GREENNOISESCALE_01			" + FormatDouble(inParams.Levels[1].GreenNoiseDivisor) + "\r\n" +
                "#define BLUENOISESCALE_01			" + FormatDouble(inParams.Levels[1].BlueNoiseDivisor) + "\r\n" +
                "#define REDNOISESCALE_02			" + FormatDouble(inParams.Levels[2].RedNoiseDivisor) + "\r\n" +
                "#define GREENNOISESCALE_02			" + FormatDouble(inParams.Levels[2].GreenNoiseDivisor) + "\r\n" +
                "#define BLUENOISESCALE_02			" + FormatDouble(inParams.Levels[2].BlueNoiseDivisor) + "\r\n" +
                "#define REDNOISESCALE_03			" + FormatDouble(inParams.Levels[3].RedNoiseDivisor) + "\r\n" +
                "#define GREENNOISESCALE_03			" + FormatDouble(inParams.Levels[3].GreenNoiseDivisor) + "\r\n" +
                "#define BLUENOISESCALE_03			" + FormatDouble(inParams.Levels[3].BlueNoiseDivisor) + "\r\n" +
                "#define REDNOISESCALE_04			" + FormatDouble(inParams.Levels[4].RedNoiseDivisor) + "\r\n" +
                "#define GREENNOISESCALE_04			" + FormatDouble(inParams.Levels[4].GreenNoiseDivisor) + "\r\n" +
                "#define BLUENOISESCALE_04			" + FormatDouble(inParams.Levels[4].BlueNoiseDivisor) + "\r\n";

            return inShaderSource.Replace("//{PARAMETERIZATION}", parameters);
        }

        private String FormatDouble(Double inValue)
        {
            String result = inValue.ToString().Replace(',', '.');

            if (!result.Contains('.'))
                result += ".0";

            return result;
        }

        public void RenderGroundPlane(Matrix4 inProjMatrix, Matrix4 inViewMatrix, Matrix4 inModelMatrix)
        {
            GL.ClearColor(Color.Black);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            // setup program and textures
            for (int i = 0; i < m_GroundTextures.Length; i++)
            {
                if (m_GroundTextures[i] == null)
                    continue;

                m_GroundTextures[i].Bind(i);
            }

            // render ground plane
            m_Shader.Bind(inProjMatrix, inViewMatrix, inModelMatrix);

            m_Mesh.RenderBlocks(ScreenBounds, false); GLRenderer.CheckError();

            GL.Clear(ClearBufferMask.DepthBufferBit);

            // render water
            m_WaterShader.Bind(inProjMatrix, inViewMatrix, inModelMatrix);
            GL.Uniform1(m_TimeMillisID, (float)PrecisionTimerCallback());

            m_Mesh.RenderBlocks(ScreenBounds, true); GLRenderer.CheckError();

            for (int i = 0; i < m_GroundTextures.Length; i++)
            {
                m_GroundTextures[i].Unbind(i);
            }
        }
    }
}
